﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading.Tasks;

namespace LujWeb.Internal;

public class PageTemplateLoader {

  public async Task<PageTemplate> LoadAsync(string path) {
    string templateRaw = await File.ReadAllTextAsync(path);
    IList<PageTemplateVar> varList = GetVarList(templateRaw);

    return new TemplateImpl(this, templateRaw, varList.ToArray());
  }

  IList<PageTemplateVar> GetVarList(string templateStr) {
    List<PageTemplateVar> result = new();

    var afterPrevNewln = (int pos) => {
      for (int i = pos - 1; i >= 0; i--) {
        if (templateStr[i] == '\n') {
          return i + 1;
        }
      }
      return 0;
    };

    var findNextLine = (int pos) => {
      for (int i = pos; i < templateStr.Length; i++) {
        if (templateStr[i] == '\n') {
          return i + 1;
        }
      }
      return templateStr.Length;
    };

    const string varOpen = "<!--@";
    const string varClose = "-->";
    int closeLen = varClose.Length;

    var findNext = (int findStart) => {
      int openIdx = templateStr.IndexOf(varOpen, findStart);
      if (openIdx < 0) {
        return templateStr.Length;
      }

      int closeIndx = templateStr.IndexOf(varClose, openIdx) + closeLen;
      int nameStart = openIdx + varOpen.Length;
      string name = templateStr[nameStart..(closeIndx - closeLen)];

      int start = afterPrevNewln(openIdx);
      int end = findNextLine(closeIndx);

      PageTemplateVar pageVar = new(name, start, end);
      //Console.WriteLine(pageVar);

      result.Add(pageVar);
      return end;
    };

    int cursor = findNext(0);
    while (cursor < templateStr.Length) {
      cursor = findNext(cursor);
    }

    return result;
  }

  #region TemplateImpl
  record TemplateImpl(PageTemplateLoader Outer, string Template, PageTemplateVar[] VarList) : PageTemplate {
    public string GetLine(string varName) => Outer.GetLineImpl(this, varName);
    public string Process(IDictionary<string, string> data) => Outer.TemplateProcessImpl(this, data);
  }
  string GetLineImpl(TemplateImpl impl, string varName) {
    return impl.VarList
      .Where(v => varName.Equals(v.Name))
      .Select(v => impl.Template[v.Start..v.End])
      .Single();
  }
  string TemplateProcessImpl(TemplateImpl impl, IDictionary<string, string> data) {
    return _templateRender.Render(impl.Template, impl.VarList, data);
  }
  #endregion

  readonly PageTemplateRender _templateRender = new();
}

public interface PageTemplate {
  public string GetLine(string varName);
  public string Process(IDictionary<string, string> data);
}

internal record PageTemplateVar(string Name, int Start, int End);
